home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Resources / Developers / XAMPP 1.5.4 / Windows installer / xampp-win32-1.5.4-installer.exe / xampp / php / pear / PEAR / Remote.php < prev    next >
Encoding:
PHP Script  |  2005-12-02  |  18.7 KB  |  520 lines

  1. <?php
  2. /**
  3.  * PEAR_Remote
  4.  *
  5.  * PHP versions 4 and 5
  6.  *
  7.  * LICENSE: This source file is subject to version 3.0 of the PHP license
  8.  * that is available through the world-wide-web at the following URI:
  9.  * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
  10.  * the PHP License and are unable to obtain it through the web, please
  11.  * send a note to license@php.net so we can mail you a copy immediately.
  12.  *
  13.  * @category   pear
  14.  * @package    PEAR
  15.  * @author     Stig Bakken <ssb@php.net>
  16.  * @author     Greg Beaver <cellog@php.net>
  17.  * @copyright  1997-2005 The PHP Group
  18.  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
  19.  * @version    CVS: $Id: Remote.php,v 1.74 2005/09/11 18:00:20 cellog Exp $
  20.  * @link       http://pear.php.net/package/PEAR
  21.  * @since      File available since Release 0.1
  22.  */
  23.  
  24. /**
  25.  * needed for PEAR_Error
  26.  */
  27. require_once 'PEAR.php';
  28. require_once 'PEAR/Config.php';
  29.  
  30. /**
  31.  * This is a class for doing remote operations against the central
  32.  * PEAR database.
  33.  *
  34.  * @nodep XML_RPC_Value
  35.  * @nodep XML_RPC_Message
  36.  * @nodep XML_RPC_Client
  37.  * @category   pear
  38.  * @package    PEAR
  39.  * @author     Stig Bakken <ssb@php.net>
  40.  * @author     Greg Beaver <cellog@php.net>
  41.  * @copyright  1997-2005 The PHP Group
  42.  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
  43.  * @version    Release: 1.4.5
  44.  * @link       http://pear.php.net/package/PEAR
  45.  * @since      Class available since Release 0.1
  46.  */
  47. class PEAR_Remote extends PEAR
  48. {
  49.     // {{{ properties
  50.  
  51.     var $config = null;
  52.     var $cache  = null;
  53.     /**
  54.      * @var PEAR_Registry
  55.      * @access private
  56.      */
  57.     var $_registry;
  58.  
  59.     // }}}
  60.  
  61.     // {{{ PEAR_Remote(config_object)
  62.  
  63.     function PEAR_Remote(&$config)
  64.     {
  65.         $this->PEAR();
  66.         $this->config = &$config;
  67.         $this->_registry = &$this->config->getRegistry();
  68.     }
  69.  
  70.     // }}}
  71.     // {{{ setRegistry()
  72.     
  73.     function setRegistry(&$reg)
  74.     {
  75.         $this->_registry = &$reg;
  76.     }
  77.     // }}}
  78.     // {{{ getCache()
  79.  
  80.  
  81.     function getCache($args)
  82.     {
  83.         $id       = md5(serialize($args));
  84.         $cachedir = $this->config->get('cache_dir');
  85.         $filename = $cachedir . DIRECTORY_SEPARATOR . 'xmlrpc_cache_' . $id;
  86.         if (!file_exists($filename)) {
  87.             return null;
  88.         }
  89.  
  90.         $fp = fopen($filename, 'rb');
  91.         if (!$fp) {
  92.             return null;
  93.         }
  94.         if (function_exists('file_get_contents')) {
  95.             fclose($fp);
  96.             $content = file_get_contents($filename);
  97.         } else {
  98.             $content  = fread($fp, filesize($filename));
  99.             fclose($fp);
  100.         }
  101.         $result   = array(
  102.             'age'        => time() - filemtime($filename),
  103.             'lastChange' => filemtime($filename),
  104.             'content'    => unserialize($content),
  105.             );
  106.         return $result;
  107.     }
  108.  
  109.     // }}}
  110.  
  111.     // {{{ saveCache()
  112.  
  113.     function saveCache($args, $data)
  114.     {
  115.         $id       = md5(serialize($args));
  116.         $cachedir = $this->config->get('cache_dir');
  117.         if (!file_exists($cachedir)) {
  118.             System::mkdir(array('-p', $cachedir));
  119.         }
  120.         $filename = $cachedir.'/xmlrpc_cache_'.$id;
  121.  
  122.         $fp = @fopen($filename, "wb");
  123.         if ($fp) {
  124.             fwrite($fp, serialize($data));
  125.             fclose($fp);
  126.         }
  127.     }
  128.  
  129.     // }}}
  130.  
  131.     // {{{ clearCache()
  132.  
  133.     function clearCache($method, $args)
  134.     {
  135.         array_unshift($args, $method);
  136.         array_unshift($args, $this->config->get('default_channel')); // cache by channel
  137.         $id       = md5(serialize($args));
  138.         $cachedir = $this->config->get('cache_dir');
  139.         $filename = $cachedir.'/xmlrpc_cache_'.$id;
  140.         if (file_exists($filename)) {
  141.             @unlink($filename);
  142.         }
  143.     }
  144.  
  145.     // }}}
  146.     // {{{ call(method, [args...])
  147.  
  148.     function call($method)
  149.     {
  150.         $_args = $args = func_get_args();
  151.  
  152.         $server_channel = $this->config->get('default_channel');
  153.         $channel = $this->_registry->getChannel($server_channel);
  154.         if ($channel) {
  155.             $mirror = $this->config->get('preferred_mirror');
  156.             if ($channel->getMirror($mirror)) {
  157.                 if ($channel->supports('xmlrpc', $method, $mirror)) {
  158.                     $server_channel = $server_host = $mirror; // use the preferred mirror
  159.                     $server_port = $channel->getPort($mirror);
  160.                 } elseif (!$channel->supports('xmlrpc', $method)) {
  161.                     return $this->raiseError("Channel $server_channel does not " .
  162.                         "support xml-rpc method $method");
  163.                 }
  164.             }
  165.             if (!isset($server_host)) {
  166.                 if (!$channel->supports('xmlrpc', $method)) {
  167.                     return $this->raiseError("Channel $server_channel does not support " .
  168.                         "xml-rpc method $method");
  169.                 } else {
  170.                     $server_host = $server_channel;
  171.                     $server_port = $channel->getPort();
  172.                 }
  173.             }
  174.         } else {
  175.             return $this->raiseError("Unknown channel '$server_channel'");
  176.         }
  177.  
  178.         array_unshift($_args, $server_channel); // cache by channel
  179.         $this->cache = $this->getCache($_args);
  180.         $cachettl = $this->config->get('cache_ttl');
  181.         // If cache is newer than $cachettl seconds, we use the cache!
  182.         if ($this->cache !== null && $this->cache['age'] < $cachettl) {
  183.             return $this->cache['content'];
  184.         }
  185.         if (extension_loaded("xmlrpc")) {
  186.             $result = call_user_func_array(array(&$this, 'call_epi'), $args);
  187.             if (!PEAR::isError($result)) {
  188.                 $this->saveCache($_args, $result);
  189.             }
  190.             return $result;
  191.         } elseif (!@include_once 'XML/RPC.php') {
  192.             return $this->raiseError("For this remote PEAR operation you need to load the xmlrpc extension or install XML_RPC");
  193.         }
  194.  
  195.         array_shift($args);
  196.         $username = $this->config->get('username');
  197.         $password = $this->config->get('password');
  198.         $eargs = array();
  199.         foreach($args as $arg) {
  200.             $eargs[] = $this->_encode($arg);
  201.         }
  202.         $f = new XML_RPC_Message($method, $eargs);
  203.         if ($this->cache !== null) {
  204.             $maxAge = '?maxAge='.$this->cache['lastChange'];
  205.         } else {
  206.             $maxAge = '';
  207.         }
  208.         $proxy_host = $proxy_port = $proxy_user = $proxy_pass = '';
  209.         if ($proxy = parse_url($this->config->get('http_proxy'))) {
  210.             $proxy_host = @$proxy['host'];
  211.             if (isset($proxy['scheme']) && $proxy['scheme'] == 'https') {
  212.                 $proxy_host = 'https://' . $proxy_host;
  213.             }
  214.             $proxy_port = @$proxy['port'];
  215.             $proxy_user = @urldecode(@$proxy['user']);
  216.             $proxy_pass = @urldecode(@$proxy['pass']);
  217.         }
  218.         $shost = $server_host;
  219.         if ($channel->getSSL()) {
  220.             $shost = "https://$shost";
  221.         }
  222.         $c = new XML_RPC_Client('/' . $channel->getPath('xmlrpc')
  223.             . $maxAge, $shost, $server_port, $proxy_host, $proxy_port,
  224.             $proxy_user, $proxy_pass);
  225.         if ($username && $password) {
  226.             $c->setCredentials($username, $password);
  227.         }
  228.         if ($this->config->get('verbose') >= 3) {
  229.             $c->setDebug(1);
  230.         }
  231.         $r = $c->send($f);
  232.         if (!$r) {
  233.             return $this->raiseError("XML_RPC send failed");
  234.         }
  235.         $v = $r->value();
  236.         if ($e = $r->faultCode()) {
  237.             if ($e == $GLOBALS['XML_RPC_err']['http_error'] && strstr($r->faultString(), '304 Not Modified') !== false) {
  238.                 return $this->cache['content'];
  239.             }
  240.             return $this->raiseError($r->faultString(), $e);
  241.         }
  242.  
  243.         $result = XML_RPC_decode($v);
  244.         $this->saveCache($_args, $result);
  245.         return $result;
  246.     }
  247.  
  248.     // }}}
  249.  
  250.     // {{{ call_epi(method, [args...])
  251.  
  252.     function call_epi($method)
  253.     {
  254.         do {
  255.             if (extension_loaded("xmlrpc")) {
  256.                 break;
  257.             }
  258.             if (OS_WINDOWS) {
  259.                 $ext = 'dll';
  260.             } elseif (PHP_OS == 'HP-UX') {
  261.                 $ext = 'sl';
  262.             } elseif (PHP_OS == 'AIX') {
  263.                 $ext = 'a';
  264.             } else {
  265.                 $ext = 'so';
  266.             }
  267.             $ext = OS_WINDOWS ? 'dll' : 'so';
  268.             @dl("xmlrpc-epi.$ext");
  269.             if (extension_loaded("xmlrpc")) {
  270.                 break;
  271.             }
  272.             @dl("xmlrpc.$ext");
  273.             if (extension_loaded("xmlrpc")) {
  274.                 break;
  275.             }
  276.             return $this->raiseError("unable to load xmlrpc extension");
  277.         } while (false);
  278.         $server_channel = $this->config->get('default_channel');
  279.         $channel = $this->_registry->getChannel($server_channel);
  280.         if ($channel) {
  281.             $mirror = $this->config->get('preferred_mirror');
  282.             if ($channel->getMirror($mirror)) {
  283.                 if ($channel->supports('xmlrpc', $method, $mirror)) {
  284.                     $server_channel = $server_host = $mirror; // use the preferred mirror
  285.                     $server_port = $channel->getPort($mirror);
  286.                 } elseif (!$channel->supports('xmlrpc', $method)) {
  287.                     return $this->raiseError("Channel $server_channel does not " .
  288.                         "support xml-rpc method $method");
  289.                 }
  290.             }
  291.             if (!isset($server_host)) {
  292.                 if (!$channel->supports('xmlrpc', $method)) {
  293.                     return $this->raiseError("Channel $server_channel does not support " .
  294.                         "xml-rpc method $method");
  295.                 } else {
  296.                     $server_host = $server_channel;
  297.                     $server_port = $channel->getPort();
  298.                 }
  299.             }
  300.         } else {
  301.             return $this->raiseError("Unknown channel '$server_channel'");
  302.         }
  303.         $params = func_get_args();
  304.         array_shift($params);
  305.         $method = str_replace("_", ".", $method);
  306.         $request = xmlrpc_encode_request($method, $params);
  307.         if ($http_proxy = $this->config->get('http_proxy')) {
  308.             $proxy = parse_url($http_proxy);
  309.             $proxy_host = $proxy_port = $proxy_user = $proxy_pass = '';
  310.             $proxy_host = @$proxy['host'];
  311.             if (isset($proxy['scheme']) && $proxy['scheme'] == 'https') {
  312.                 $proxy_host = 'ssl://' . $proxy_host;
  313.             }
  314.             $proxy_port = @$proxy['port'];
  315.             $proxy_user = @urldecode(@$proxy['user']);
  316.             $proxy_pass = @urldecode(@$proxy['pass']);
  317.             $fp = @fsockopen($proxy_host, $proxy_port);
  318.             $use_proxy = true;
  319.             if ($channel->getSSL()) {
  320.                 $server_host = "https://$server_host";
  321.             }
  322.         } else {
  323.             $use_proxy = false;
  324.             $ssl = $channel->getSSL();
  325.             $fp = @fsockopen(($ssl ? 'ssl://' : '') . $server_host, $server_port);
  326.             if (!$fp) {
  327.                 $server_host = "$ssl$server_host"; // for error-reporting
  328.             }
  329.         }
  330.         if (!$fp && $http_proxy) {
  331.             return $this->raiseError("PEAR_Remote::call: fsockopen(`$proxy_host', $proxy_port) failed");
  332.         } elseif (!$fp) {
  333.             return $this->raiseError("PEAR_Remote::call: fsockopen(`$server_host', $server_port) failed");
  334.         }
  335.         $len = strlen($request);
  336.         $req_headers = "Host: $server_host:$server_port\r\n" .
  337.              "Content-type: text/xml\r\n" .
  338.              "Content-length: $len\r\n";
  339.         $username = $this->config->get('username');
  340.         $password = $this->config->get('password');
  341.         if ($username && $password) {
  342.             $req_headers .= "Cookie: PEAR_USER=$username; PEAR_PW=$password\r\n";
  343.             $tmp = base64_encode("$username:$password");
  344.             $req_headers .= "Authorization: Basic $tmp\r\n";
  345.         }
  346.         if ($this->cache !== null) {
  347.             $maxAge = '?maxAge='.$this->cache['lastChange'];
  348.         } else {
  349.             $maxAge = '';
  350.         }
  351.  
  352.         if ($use_proxy && $proxy_host != '' && $proxy_user != '') {
  353.             $req_headers .= 'Proxy-Authorization: Basic '
  354.                 .base64_encode($proxy_user.':'.$proxy_pass)
  355.                 ."\r\n";
  356.         }
  357.  
  358.         if ($this->config->get('verbose') > 3) {
  359.             print "XMLRPC REQUEST HEADERS:\n";
  360.             var_dump($req_headers);
  361.             print "XMLRPC REQUEST BODY:\n";
  362.             var_dump($request);
  363.         }
  364.  
  365.         if ($use_proxy && $proxy_host != '') {
  366.             $post_string = "POST http://".$server_host;
  367.             if ($proxy_port > '') {
  368.                 $post_string .= ':'.$server_port;
  369.             }
  370.         } else {
  371.             $post_string = "POST ";
  372.         }
  373.  
  374.         $path = '/' . $channel->getPath('xmlrpc');
  375.         fwrite($fp, ($post_string . $path . "$maxAge HTTP/1.0\r\n$req_headers\r\n$request"));
  376.         $response = '';
  377.         $line1 = fgets($fp, 2048);
  378.         if (!preg_match('!^HTTP/[0-9\.]+ (\d+) (.*)!', $line1, $matches)) {
  379.             return $this->raiseError("PEAR_Remote: invalid HTTP response from XML-RPC server");
  380.         }
  381.         switch ($matches[1]) {
  382.             case "200": // OK
  383.                 break;
  384.             case "304": // Not Modified
  385.                 return $this->cache['content'];
  386.             case "401": // Unauthorized
  387.                 if ($username && $password) {
  388.                     return $this->raiseError("PEAR_Remote ($server_host:$server_port) " .
  389.                         ": authorization failed", 401);
  390.                 } else {
  391.                     return $this->raiseError("PEAR_Remote ($server_host:$server_port) " .
  392.                         ": authorization required, please log in first", 401);
  393.                 }
  394.             default:
  395.                 return $this->raiseError("PEAR_Remote ($server_host:$server_port) : " .
  396.                     "unexpected HTTP response", (int)$matches[1], null, null,
  397.                     "$matches[1] $matches[2]");
  398.         }
  399.         while (trim(fgets($fp, 2048)) != ''); // skip rest of headers
  400.         while ($chunk = fread($fp, 10240)) {
  401.             $response .= $chunk;
  402.         }
  403.         fclose($fp);
  404.         if ($this->config->get('verbose') > 3) {
  405.             print "XMLRPC RESPONSE:\n";
  406.             var_dump($response);
  407.         }
  408.         $ret = xmlrpc_decode($response);
  409.         if (is_array($ret) && isset($ret['__PEAR_TYPE__'])) {
  410.             if ($ret['__PEAR_TYPE__'] == 'error') {
  411.                 if (isset($ret['__PEAR_CLASS__'])) {
  412.                     $class = $ret['__PEAR_CLASS__'];
  413.                 } else {
  414.                     $class = "PEAR_Error";
  415.                 }
  416.                 if ($ret['code']     === '') $ret['code']     = null;
  417.                 if ($ret['message']  === '') $ret['message']  = null;
  418.                 if ($ret['userinfo'] === '') $ret['userinfo'] = null;
  419.                 if (strtolower($class) == 'db_error') {
  420.                     $ret = $this->raiseError(PEAR::errorMessage($ret['code']),
  421.                                              $ret['code'], null, null,
  422.                                              $ret['userinfo']);
  423.                 } else {
  424.                     $ret = $this->raiseError($ret['message'], $ret['code'],
  425.                                              null, null, $ret['userinfo']);
  426.                 }
  427.             }
  428.         } elseif (is_array($ret) && sizeof($ret) == 1 && isset($ret[0])
  429.                   && is_array($ret[0]) &&
  430.                   !empty($ret[0]['faultString']) &&
  431.                   !empty($ret[0]['faultCode'])) {
  432.             extract($ret[0]);
  433.             $faultString = "XML-RPC Server Fault: " .
  434.                  str_replace("\n", " ", $faultString);
  435.             return $this->raiseError($faultString, $faultCode);
  436.         } elseif (is_array($ret) && sizeof($ret) == 2 && !empty($ret['faultString']) &&
  437.               !empty($ret['faultCode'])) {
  438.             extract($ret);
  439.             $faultString = "XML-RPC Server Fault: " .
  440.                  str_replace("\n", " ", $faultString);
  441.             return $this->raiseError($faultString, $faultCode);
  442.         }
  443.         return $ret;
  444.     }
  445.  
  446.     // }}}
  447.  
  448.     // {{{ _encode
  449.  
  450.     // a slightly extended version of XML_RPC_encode
  451.     function _encode($php_val)
  452.     {
  453.         global $XML_RPC_Boolean, $XML_RPC_Int, $XML_RPC_Double;
  454.         global $XML_RPC_String, $XML_RPC_Array, $XML_RPC_Struct;
  455.  
  456.         $type = gettype($php_val);
  457.         $xmlrpcval = new XML_RPC_Value;
  458.  
  459.         switch($type) {
  460.             case "array":
  461.                 reset($php_val);
  462.                 $firstkey = key($php_val);
  463.                 end($php_val);
  464.                 $lastkey = key($php_val);
  465.                 reset($php_val);
  466.                 if ($firstkey === 0 && is_int($lastkey) &&
  467.                     ($lastkey + 1) == count($php_val)) {
  468.                     $is_continuous = true;
  469.                     reset($php_val);
  470.                     $size = count($php_val);
  471.                     for ($expect = 0; $expect < $size; $expect++, next($php_val)) {
  472.                         if (key($php_val) !== $expect) {
  473.                             $is_continuous = false;
  474.                             break;
  475.                         }
  476.                     }
  477.                     if ($is_continuous) {
  478.                         reset($php_val);
  479.                         $arr = array();
  480.                         while (list($k, $v) = each($php_val)) {
  481.                             $arr[$k] = $this->_encode($v);
  482.                         }
  483.                         $xmlrpcval->addArray($arr);
  484.                         break;
  485.                     }
  486.                 }
  487.                 // fall though if not numerical and continuous
  488.             case "object":
  489.                 $arr = array();
  490.                 while (list($k, $v) = each($php_val)) {
  491.                     $arr[$k] = $this->_encode($v);
  492.                 }
  493.                 $xmlrpcval->addStruct($arr);
  494.                 break;
  495.             case "integer":
  496.                 $xmlrpcval->addScalar($php_val, $XML_RPC_Int);
  497.                 break;
  498.             case "double":
  499.                 $xmlrpcval->addScalar($php_val, $XML_RPC_Double);
  500.                 break;
  501.             case "string":
  502.             case "NULL":
  503.                 $xmlrpcval->addScalar($php_val, $XML_RPC_String);
  504.                 break;
  505.             case "boolean":
  506.                 $xmlrpcval->addScalar($php_val, $XML_RPC_Boolean);
  507.                 break;
  508.             case "unknown type":
  509.             default:
  510.                 return null;
  511.         }
  512.         return $xmlrpcval;
  513.     }
  514.  
  515.     // }}}
  516.  
  517. }
  518.  
  519. ?>
  520.